package com.cjh.mybatis11.executor.resultset;

import com.cjh.mybatis11.executor.Executor;
import com.cjh.mybatis11.executor.result.DefaultResultContext;
import com.cjh.mybatis11.executor.result.DefaultResultHandler;
import com.cjh.mybatis11.mapping.BoundSql;
import com.cjh.mybatis11.mapping.MappedStatement;
import com.cjh.mybatis11.mapping.ResultMap;
import com.cjh.mybatis11.mapping.ResultMapping;
import com.cjh.mybatis11.reflection.MetaClass;
import com.cjh.mybatis11.reflection.MetaObject;
import com.cjh.mybatis11.reflection.factory.ObjectFactory;
import com.cjh.mybatis11.session.Configuration;
import com.cjh.mybatis11.session.ResultHandler;
import com.cjh.mybatis11.session.RowBounds;
import com.cjh.mybatis11.type.TypeHandler;
import com.cjh.mybatis11.type.TypeHandlerRegistry;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * @Descripton 默认map结果处理器
 * @Author cjh
 * @Date 2023/6/1 10:28
 */
public class DefaultResultSetHandler implements ResultSetHandler {
    private final Configuration configuration;
    private final MappedStatement mappedStatement;
    private final RowBounds rowBounds;
    private final ResultHandler resultHandler;

    private final BoundSql boundSql;
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final ObjectFactory objectFactory;


    public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement,
                                   ResultHandler resultHandler, RowBounds rowBounds, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.rowBounds = rowBounds;


        this.boundSql = boundSql;
        this.mappedStatement = mappedStatement;

        this.resultHandler = resultHandler;
        this.objectFactory = configuration.getObjectFactory();
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Object> handleResultSets(Statement statement) throws SQLException {
        final List<Object> multipleResults = new ArrayList<>();
        int resultSetCount = 0;

        ResultSetWrapper resultSetWrapper = new ResultSetWrapper(statement.getResultSet(), configuration);
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();

        while (resultSetWrapper != null && resultMaps.size() > resultSetCount) {
            ResultMap resultMap = resultMaps.get(resultSetCount);
            handleResultSet(resultSetWrapper, resultMap, multipleResults, null);
            resultSetWrapper = getNextResultSet(statement);
            resultSetCount++;
        }

        return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;

    }

    private ResultSetWrapper getNextResultSet(Statement statement) throws SQLException {
        // Making this method tolerant of bad JDBC drivers
        try {
            if (statement.getConnection().getMetaData().supportsMultipleResultSets()) {
                // Crazy Standard JDBC way of determining if there are more results
                if (!((!statement.getMoreResults()) && (statement.getUpdateCount() == -1))) {
                    ResultSet resultSet = statement.getResultSet();
                    return resultSet != null ? new ResultSetWrapper(resultSet, configuration) : null;
                }
            }
        } catch (Exception ignore) {
            // Intentionally ignored.


        }
        return null;
    }

    private void handleResultSet(ResultSetWrapper resultSetWrapper, ResultMap resultMap,
                                 List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        if (resultHandler == null) {
            // 1. 新创建结果处理器
            DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
            // 2. 封装数据
            handleRowValuesForSimpleResultMap(resultSetWrapper,resultMap,defaultResultHandler,rowBounds,null);
            // 3. 保存结果
            multipleResults.add(defaultResultHandler.getResultList());
        }
    }

    private void handleRowValuesForSimpleResultMap(ResultSetWrapper resultSetWrapper,
                                                   ResultMap resultMap, ResultHandler resultHandler,
                                                   RowBounds rowBounds, ResultMapping resultMapping)
            throws SQLException {
        DefaultResultContext defaultResultContext = new DefaultResultContext();
        while (defaultResultContext.getResultCount() < rowBounds.getLimit()
                && resultSetWrapper.getResultSet().next()) {
            Object rowValue = getRowValue(resultSetWrapper,resultMap);
            callResultHandler(resultHandler,defaultResultContext,rowValue);
        }

    }

    private void callResultHandler(ResultHandler resultHandler, DefaultResultContext resultContext,
                                   Object rowValue) {
        resultContext.nextResultObject(rowValue);
        resultHandler.handleResult(resultContext);
    }

    /**
     * 获取一行的值
     *
     * @param resultSetWrapper
     * @param resultMap
     * @return
     * @throws SQLException
     */
    private Object getRowValue(ResultSetWrapper resultSetWrapper, ResultMap resultMap) throws SQLException {
        // 根据返回类型，实例化对象
        Object resultObject = createResultObject(resultSetWrapper, resultMap, null);
        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(resultObject);
            applyAutomaticMappings(resultSetWrapper, resultMap, metaObject, null);
        }
        return resultObject;


    }


    private Object createResultObject(ResultSetWrapper resultSetWrapper, ResultMap resultMap, String columnPrefix)
            throws SQLException {
        final List<Class<?>> constructorArgTypes = new ArrayList<>();
        final List<Object> constructorArgs = new ArrayList<>();
        return createResultObject(resultSetWrapper, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    }

    /**
     * 创建结果
     *
     * @param resultSetWrapper
     * @param resultMap
     * @param constructorArgTypes
     * @param constructorArgs
     * @param columnPrefix
     * @return
     * @throws SQLException
     */
    private Object createResultObject(ResultSetWrapper resultSetWrapper, ResultMap resultMap,
                                      List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
                                      String columnPrefix) throws SQLException {

        final Class<?> resultType = resultMap.getType();
        final MetaClass metaType = MetaClass.forClass(resultType);

        if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
            // 普通的Bean对象类型
            return objectFactory.create(resultType);
        }
        throw new RuntimeException("Do not know how to create an instance of " + resultType);

    }

    private boolean applyAutomaticMappings(ResultSetWrapper resultSetWrapper, ResultMap resultMap,
                                           MetaObject metaObject, String columnPrefix) throws SQLException {
        final List<String> unmappedColumnNames = resultSetWrapper.getUnmappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        for (String columnName : unmappedColumnNames) {
            String propertyName = columnName;
            if (columnPrefix != null && !columnPrefix.isEmpty()) {
                // When columnPrefix is specified,ignore columns without the prefix.
                if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
                    propertyName = columnName.substring(columnPrefix.length());
                } else {
                    continue;
                }
            }
            final String property = metaObject.findProperty(propertyName, false);
            if (property != null && metaObject.hasGetter(property)) {
                final Class<?> propertyType = metaObject.getSetterType(property);
                if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
                    final TypeHandler<?> typeHandler = resultSetWrapper.getTypeHandler(propertyType, columnName);
                    // 使用 TypeHandler 取得结果
                    final Object value = typeHandler.getResult(resultSetWrapper.getResultSet(), columnName);
                    if (value != null) {
                        foundValues = true;
                    }
                    if (value != null || !propertyType.isPrimitive()) {
                        // 通过反射工具类设置属性值
                        metaObject.setValue(property, value);
                    }
                }

            }
        }
        return foundValues;

    }


}
